package com.hycat.mail.product.service.impl;

import com.hycat.mail.product.dao.AttrGroupDao;
import com.hycat.mail.product.entity.AttrGroupEntity;
import com.hycat.mail.product.service.CategoryBrandRelationService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hycat.mail.common.utils.PageUtil;
import com.hycat.mail.common.utils.Query;

import com.hycat.mail.product.dao.CategoryDao;
import com.hycat.mail.product.entity.CategoryEntity;
import com.hycat.mail.product.service.CategoryService;
import org.springframework.util.StringUtils;


@Service("categoryService")
@Slf4j
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redisson;

    private List<Long> categoryPath = new ArrayList();

    @Autowired
    private CategoryDao categoryDao;

    @Autowired
    private AttrGroupDao attrGroupDao;

    @Autowired
    private CategoryBrandRelationService categoryBrandRelationService;

    @Override
    public PageUtil queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtil(page);
    }

    @Override
    @Cacheable(cacheNames = "category3List", key = "'category3List'")
    public List<CategoryEntity> listTree() {
        List<CategoryEntity> list = list();
        List<CategoryEntity> listTree = list().stream()
                .filter((category) -> (category.getParentCid() == 0))
                .map((category) -> {
                    category.setChildren(findChildren(list, category));
                    return category;
                })
                .sorted(Comparator.comparingInt(o -> (o.getSort() == null ? 0 : o.getSort())))
                .collect(Collectors.toList());

        return listTree;
    }

    @Override
    public void removeCategoryByIds(List<Long> asList) {
        //TODO 1.检查当前分类是否被引用
        boolean result = removeByIds(asList);

        //TODO result异常处理
    }

    @Override
    public void updateDetails(CategoryEntity category) {
        this.updateById(category);
        //对品牌分类关联表进行修改
        if(StringUtils.isEmpty(category.getName())) {
            categoryBrandRelationService.updateDetails(category.getCatId(), category.getName(), "category");
        }
    }

    @Override
    public List<Long> categoryPath(Long attrGroupId) {
        //清空缓存
        categoryPath.clear();

        AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrGroupId);
        Long catelogId = attrGroupEntity.getCatelogId();

        for(int i = 0; i < 3; i++) {
            categoryPath.add(catelogId);
            CategoryEntity categoryEntity = categoryDao.selectById(catelogId);
            catelogId = categoryEntity.getParentCid();
        }
        Collections.reverse(categoryPath);

        return categoryPath;
    }

    //递归查找root孩子
    public List<CategoryEntity> findChildren(List<CategoryEntity> categoryList, CategoryEntity root) {
        //对categoryList的孩子进行注入
        return categoryList.stream()
                .filter((category) -> (root.getCatId().equals(category.getParentCid())))
                .map((category) -> {
                    category.setChildren(findChildren(categoryList, category));
                    return category;
                })
                .sorted(Comparator.comparingInt(o -> (o.getSort() == null ? 0 : o.getSort())))
                .collect(Collectors.toList());
    }

    //分布式锁之方法一：setIfAbsent方法自旋
    public void spin() {
        String uuid = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);
        if(lock) {
            //加锁成功 执行业务

            //删锁
            String lockValue = (String) redisTemplate.opsForValue().get("lock");
            if(uuid.equals(lockValue)) {
                redisTemplate.delete("lock");
            }
        } else {
            //不断重试  自旋
        }
    }

    //分布式锁之方法二：redisson阻塞时等待
    public void redisson() {
        RLock categoryRluck = redisson.getLock("categoryRluck");
        //categoryRluck.lock(3, TimeUnit.SECONDS);
        // 尝试加锁，最多等待 100 秒，上锁以后 10 秒自动解锁 boolean res = lock.tryLock(100,10, TimeUnit.SECONDS);
        try {
            boolean tryLock = categoryRluck.tryLock(100, 3, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            categoryRluck.unlock();
        }
    }

    //缓存一致性问题：
}